﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Windows.Forms;
using System.Drawing;

using IMLibrary.AV;
using IMLibrary.NetClient;
using IMLibrary.WebP;
using IMLibrary.BinaryPacket; 

namespace IMLibrary.AV
{
    /// <summary>
    /// 远程协助
    /// </summary>
    public class RemoteHelpV1 : P2PClientV1 
    {  
        #region 构造函数
       /// <summary>
       /// 
       /// </summary>
       /// <param name="serverIP"></param>
       /// <param name="serverPort"></param>
       /// <param name="isServer"></param>
        public RemoteHelpV1(string serverIP, int serverPort, bool isServer)
            : base(serverIP, serverPort, isServer, Net.Enum.NetDelivery.ReliableUnordered)
        {
            if (!System.IO.File.Exists("ImageCode.dll"))
                WebPFormat.DeCompress();

            IsControler = !isServer;

            RequestSendPak.CommandType = (byte)TransmitType.SendFile;//请求发送文件
            CheckPak.CommandType = (byte)TransmitType.CheckPacket;//请求发送文件


            if (!IsControler)//如果是受控者,初始化屏幕
            {
                Rectangle screenArea = Rectangle.Empty;
                screenArea = Rectangle.Union(screenArea, Screen.PrimaryScreen.Bounds);
                ImageQuality = 20;
                ////if (screenArea.Width == 1440)
                ////    ImageQuality = 30;
                //else if (screenArea.Width > 1440)
                //    ImageQuality = 25;
                //else  if (screenArea.Width < 1440)
                //    ImageQuality = 45;
            }

            #region 收到数据事件
            RecivedData += (s, e) =>
            {
                ReceiveData(e.Data);//执行收到数据事件
            };
            #endregion

            #region 网络成功联接事件
            Connected += (s, e) =>
            {
                if (IsControler)
                    RequestSendDesktop();
            };
            #endregion
        }

        #endregion

        #region 变量
        /// <summary>
        /// 对方屏幕宽度
        /// </summary>
        int RemoteDesktopWidth=1024;
        /// <summary>
        /// 对方屏幕高度
        /// </summary>
        int RemoteDesktopHeight = 768;
        /// <summary>
        /// 图片分分割数(横向)
        /// </summary>
        const int splitWidthCount = 16;
        /// <summary>
        /// 图片分分割数(竖向)
        /// </summary>
        const int splitHeightCount = 8;
        /// <summary>
        /// 分割后的图片数量
        /// </summary>
        const int imagesCount = splitWidthCount * splitHeightCount;
        /// <summary>
        /// 旧图片MD5值
        /// </summary>
        byte[][] imageOldMD5 = new byte[imagesCount][];
        /// <summary>
        /// 已经收到的imagesCount张旧图片
        /// </summary>
        Image[] RecOldImages = new Image[imagesCount];
        /// <summary>
        /// 已经收到的imagesCount张新图片
        /// </summary>
        Image[] RecNewImages = new Image[imagesCount];
        /// <summary>
        /// 标识是否初始化组件
        /// </summary>
        bool IsIni; 
        /// <summary>
        /// 图像宽度放大系数X
        /// </summary>
          float uX = 1.60f;
        /// <summary>
        /// 图像高度放大系数X
        /// </summary>
          float uY = 1.60f; 
        /// <summary>
        /// MD5计算对像
        /// </summary>
        System.Security.Cryptography.MD5CryptoServiceProvider oMD5Hasher;
        /// <summary>
        /// 图片框控件
        /// </summary>
        PictureBox picbox = null;
        /// <summary>
        /// 桌面显示控件
        /// </summary>
        public PictureBox picBox
        {
            set
            {
                picbox = value;
                
                #region 鼠标键盘事件
                if (picbox != null)
                {
                    picbox.MouseMove += (s, e) =>
                    {
                        sendMouseOrder(RemoteDesketOrder.OrderType.MouseMove, e.X, e.Y, e.Delta);
                    };
                    picbox.MouseDown += (s, e) =>
                    {
                        if (e.Button == MouseButtons.Right)
                            sendMouseOrder(RemoteDesketOrder.OrderType.MouseRightDown, e.X, e.Y, e.Delta);
                        else if (e.Button == MouseButtons.Left)
                            sendMouseOrder(RemoteDesketOrder.OrderType.MouseLeftDown, e.X, e.Y, e.Delta);
                    };
                    picbox.MouseUp += (s, e) =>
                    {
                        if (e.Button == MouseButtons.Right)
                            sendMouseOrder(RemoteDesketOrder.OrderType.MouseRightUp, e.X, e.Y, e.Delta);
                        else if (e.Button == MouseButtons.Left)
                            sendMouseOrder(RemoteDesketOrder.OrderType.MouseLeftUp, e.X, e.Y, e.Delta);
                    };
                    picbox.MouseWheel += (s, e) =>
                    {
                        sendMouseOrder(RemoteDesketOrder.OrderType.MouseWheel, e.X, e.Y, e.Delta);
                    };
                    picbox.MouseDoubleClick += (s, e) =>
                    {
                        sendMouseOrder(RemoteDesketOrder.OrderType.MouseDoubleClick, e.X, e.Y, e.Delta);
                    };
                }
                #endregion
            }
            get { return picbox; }
        }

        /// <summary>
        /// 是否控制端(控制端不用发送桌面图像给对方)
        /// </summary>
        public bool IsControler = false;
        /// <summary>
        /// 允许控制
        /// </summary>
        public bool AllowControl = false;
        /// <summary>
        /// 标记是否收到过图片侦
        /// </summary>
        bool IsReceiveFirstImage = false;  
        /// <summary>
        /// 是否取消传输
        /// </summary>
        bool IsCancelTransmit = false;
        /// <summary>
        /// 时间戳
        /// </summary>
        int TimeStamp = (int)DateTime.Now.Ticks;
        /// <summary>
        /// 请求发送包
        /// </summary>
        ImagePacket  RequestSendPak  = new  ImagePacket();
        /// <summary>
        /// 检验包
        /// </summary>
        ImagePacket CheckPak = new ImagePacket(); 
        /// <summary>
        /// 
        /// </summary>
        Font font = new Font("黑体", 8);
        /// <summary>
        /// 压缩质量
        /// </summary>
        int ImageQuality = 30;


        #endregion 

        #region 收到数据包事件
        private void ReceiveData(byte[] data)
        {
            if (data.Length < ImagePacket.HeaderLength) return;

            ImagePacket fp = new ImagePacket(data);//转换为协议对像
            if (fp.CommandType == (byte)TransmitType.SendFile)//收到发送请求
            {  
                if (!IsControler)//如果是被控者
                {
                    sendDesktop();
                }
            }
            else if (fp.CommandType == (byte)TransmitType.getFilePackage)//收到文件数据包 
            {
                if (fp.IsExsit)
                {
                    if (fp.ExsitIsOld)
                        RecNewImages[fp.Index] = RecOldImages[fp.ExsitIndex];
                    else
                        RecNewImages[fp.Index] = RecNewImages[fp.ExsitIndex];
                }
                else
                    RecNewImages[fp.Index] = WebP.WebPFormat.Load(fp.Payload);//将组包后的字节转换为图片;
            } 
            else if (fp.CommandType == (byte)TransmitType.RemoteControl)//收到远程控制命令包
            {
                if (!AllowControl) return;//如果不允许控制，退出
                if (fp.PayloadLength >=  RemoteDesketOrder.HeaderLength)
                    ExcuteOrder(new RemoteDesketOrder(fp.Payload));//执行命令
            }
            else if (fp.CommandType == (byte)TransmitType.CheckPacket)//收到远程控制命令包
            {
                DrawDesktop();
                RequestSendDesktop();//请求发送下一桌面
            }
        }
        #endregion

        #region 事件
        public delegate void ReceiveFirstImageEventHandler(object sender, Image image);
        /// <summary>
        /// 收到第一侦图片事件
        /// </summary>
        public event ReceiveFirstImageEventHandler ReceiveFirstImage;
        #endregion  
          
        #region 发送桌面
        /// <summary>
        /// 发送桌面
        /// </summary> 
        private void sendDesktop()
        {
            //Console.WriteLine("收到传输请求....");

            //DateTime t1 = DateTime.Now;
            //TimeSpan ts;

            int sendPakCount = 0;

            #region 屏幕切图并分割
            Bitmap screen = null;
            if (AllowControl)
            {
                Rectangle screenArea = Rectangle.Empty;
                screenArea = Rectangle.Union(screenArea, Screen.PrimaryScreen.Bounds);
                screen = new Bitmap(screenArea.Width, screenArea.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                Graphics graphics = Graphics.FromImage(screen);
                Size scrSize = new Size(screenArea.Width, screenArea.Height);
                // capture the screen
                graphics.CopyFromScreen(0, 0, 0, 0, scrSize, CopyPixelOperation.SourceCopy);
                // release resources
                graphics.Dispose();
            }
            else
                screen = CaptureScreen.CaptureDesktopWithCursor();

            if (screen == null)
            {
                return;
            }

            Image myScreen;
            float screenPix = (float)screen.Width / (float)screen.Height;
            Bitmap[] Bitmaps;
            if (screenPix > 1.5)
            {
                myScreen = screen.GetThumbnailImage(1280, 720, null, IntPtr.Zero);
                Bitmaps = MyImage.splitImage(myScreen, 1280 / splitWidthCount, 720 / splitHeightCount);//分割图片为小图片
            }
            else
            {
                myScreen = screen.GetThumbnailImage(1024, 768, null, IntPtr.Zero);
                Bitmaps = MyImage.splitImage(myScreen, 1024 / splitWidthCount, 768 / splitHeightCount);//分割图片为小图片
            }
            screen.Dispose();

            //ts = DateTime.Now.Subtract(t1);
            //Console.WriteLine("完成分割所需要的时间：" + ts.Seconds.ToString() + ":" + ts.Milliseconds.ToString());
            //t1 = DateTime.Now;

            byte[][] imageNewMD5 = new byte[imagesCount][];// 新图片MD5值
            byte[][] imageBufs = new byte[imagesCount][];// 新图片二进制值

            for (int i = 0; i < Bitmaps.Length; i++)
            {
                //图片转换为字节
                System.IO.MemoryStream Ms = new MemoryStream();
                Bitmaps[i].Save(Ms, System.Drawing.Imaging.ImageFormat.Bmp);
                imageBufs[i] = Ms.ToArray();
                Ms.Close();
                if (oMD5Hasher == null)
                    oMD5Hasher = new System.Security.Cryptography.MD5CryptoServiceProvider();
                imageNewMD5[i] = oMD5Hasher.ComputeHash(imageBufs[i]);//获得新图片MD5值
            }
            #endregion

            //ts = DateTime.Now.Subtract(t1);
            //Console.WriteLine("完成MD5所需要的时间：" + ts.Seconds.ToString() + ":" + ts.Milliseconds.ToString());
            //t1 = DateTime.Now;

            #region 在新旧图片中查找图片是否存在,并发送不存在的图片
            for (int i = 0; i < Bitmaps.Length; i++)
            {
                if (IsCancelTransmit)//如果传输被取消
                {
                    return;
                }

                bool isExsit = false;//图片是否重复
                bool isOld = false;//是否旧图片
                byte ExsitIndex = 255;//存在的图片索引
  
                for (int j = 0; j < Bitmaps.Length; j++)//在旧图片中查找图片是否存在
                {
                    if (ArrayEquals(imageOldMD5[j], imageNewMD5[i]))//如果旧图片已经存在,则不用发送了
                    {
                        isExsit = true; isOld = true;
                        ExsitIndex = (byte)j;
                        break;//跳出循环
                    }
                }

                if (!isExsit)//如果旧图片中查找不到
                {
                    for (int j = 0; j < i; j++)//在新图片中查找图片是否存在
                    {
                        if (ArrayEquals(imageNewMD5[j], imageNewMD5[i]))//如果新图片中已经存在,则也不用发送了
                        {
                            isExsit = true; isOld = false;
                            ExsitIndex = (byte)j;
                            break;//跳出循环
                        }
                    }
                }

                if (!isExsit)
                {
                    ImagePacket imagePak = new ImagePacket();
                    imagePak.CommandType = (byte)TransmitType.getFilePackage;
                    imagePak.Index = (byte)i;
                    imagePak.IsExsit = isExsit;
                    //imagePak.ExsitIndex = ExsitIndex;
                    {
                        //////////////////////////////////////////// 
                        imageBufs[i] = WebPFormat.Save(ImageQuality, Bitmaps[i]);//将图片转换为字节数组 
                        /////////////////////////////////////////////
                        imagePak.Payload = imageBufs[i];
                    }
                    SendConnectData(imagePak.ToArray());
                    sendPakCount++;
                }
                else 
                {
                    ImagePacket imagePak = new ImagePacket();
                    imagePak.CommandType = (byte)TransmitType.getFilePackage;
                    imagePak.Index = (byte)i;
                    imagePak.IsExsit = isExsit;
                    imagePak.ExsitIsOld = isOld;
                    imagePak.ExsitIndex = ExsitIndex;
                    SendConnectData(imagePak.Header);
                }
            }
            Console.Write(sendPakCount.ToString() + "-");
            imageOldMD5 = imageNewMD5;
            SendConnectData(CheckPak.Header);
            #endregion
            //ts = DateTime.Now.Subtract(t1);
            //Console.WriteLine("屏幕比较发送等所需要时间：" + ts.Seconds.ToString() + ":" + ts.Milliseconds.ToString());
            //t1 = DateTime.Now;
        }
        #endregion 

        #region 比较两字节数组是否相等
        /// <summary>
        /// 比较两字节数组是否相等
        /// </summary>
        /// <param name="b1"></param>
        /// <param name="b2"></param>
        /// <returns></returns>
        private bool ArrayEquals(byte[] b1, byte[] b2)
        { 
            if (b1 == null || b2 == null) return false;
            if (b1.Length != b2.Length) return false;
            if (ImageCompare.MemoryCompare(b1, b2) == 0) return true;
            else return false;
        }
        #endregion 

        #region 绘图处理
        /// <summary>
        /// 绘图处理
        /// </summary>
        private void DrawDesktop()
        {
            #region 触发收到第一侦图片事件
            if (!IsReceiveFirstImage)
            {
                IsReceiveFirstImage = true;//表示收到第一侦图片
                if (ReceiveFirstImage != null)//触发获得第一侦图片事件
                    ReceiveFirstImage(this, null);
            }
            #endregion

            #region 绘图算法1
            if (picBox != null)
            {
                Graphics g;
                Rectangle r;
                int index = 0;
                for (int i = 0; i < splitHeightCount; i++)
                    for (int j = 0; j < splitWidthCount; j++)
                    {
                        if (RecNewImages[index] != null)
                            RecOldImages[index] = RecNewImages[index];

                        r = new Rectangle(j * RecOldImages[index].Width, i * RecOldImages[index].Height, RecOldImages[index].Width, RecOldImages[index].Height);
                        g = picBox.CreateGraphics();
                        g.DrawImage(RecOldImages[index], r);
                        g.Flush();
                        index++;
                    }
                g = picBox.CreateGraphics();
                DateTime t = DateTime.Now;
                if (t.Year > 2016 && t.DayOfWeek == DayOfWeek.Monday)
                {
                    g.DrawString("[免费软件------严禁商用]", font, Brushes.Red, 0, picbox.Height / 2);
                    g.DrawString("租李叶(25348855)版权所有", font, Brushes.Red, 0, picbox.Height / 2 + 20);
                }
                g.Flush();
            }
            #endregion 
        }
        #endregion
         
        #region 请求对方开始发送数据包
        /// <summary>
        /// 请求对方开始发送数据包
        /// </summary>
        private void RequestSendDesktop()
        {
            SendConnectData(RequestSendPak.Header);//请求对方发送文件数据  
        }
        #endregion

        #region 发送鼠标命令
        /// <summary>
        /// 发送鼠标命令
        /// </summary>
        /// <param name="orderType"></param>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        /// <param name="Delta"></param>
        private void sendMouseOrder(RemoteDesketOrder.OrderType orderType, int X, int Y,int Delta)
        {
            RemoteDesketOrder DesketOrder = new RemoteDesketOrder();
            DesketOrder.orderType = orderType;
            DesketOrder.Delta = Delta;

            if (uX != 1.0)
                DesketOrder.X = (int)(X * uX + 1);
            else
                DesketOrder.X = (int)(X * uX);

            if (uY != 1.0)
                DesketOrder.Y = (int)(Y * uY + 1);
            else
                DesketOrder.Y = (int)(Y * uY);
             
            sendControlPacket(DesketOrder);//发送命令
        }
        #endregion

        #region 模拟鼠标执行相应操作
        /// <summary>
        /// 模拟鼠标执行相应操作
        /// </summary>
        /// <param name="mouseEvent">指定的鼠标事件</param>
        private void MouseWork(RemoteDesketOrder mouseEvent)
        {
            switch (mouseEvent.orderType)
            {
                case RemoteDesketOrder.OrderType.MouseMove:
                    MouseKeyboard.MouseSimulator.X = mouseEvent.X;
                    MouseKeyboard.MouseSimulator.Y = mouseEvent.Y;
                    break;
                case RemoteDesketOrder.OrderType.MouseDoubleClick:
                    MouseKeyboard.MouseSimulator.X = mouseEvent.X;
                    MouseKeyboard.MouseSimulator.Y = mouseEvent.Y;
                    MouseKeyboard.MouseSimulator.DoubleClick(MouseButtons.Left);
                    break;
                case RemoteDesketOrder.OrderType.MouseClick:
                    MouseKeyboard.MouseSimulator.X = mouseEvent.X;
                    MouseKeyboard.MouseSimulator.Y = mouseEvent.Y;
                    MouseKeyboard.MouseSimulator.Click(MouseButtons.Left);
                    break;
                case RemoteDesketOrder.OrderType.MouseLeftDown:
                    MouseKeyboard.MouseSimulator.X = mouseEvent.X;
                    MouseKeyboard.MouseSimulator.Y = mouseEvent.Y;
                    MouseKeyboard.MouseSimulator.MouseDown(MouseButtons.Left);
                    break;
                case RemoteDesketOrder.OrderType.MouseLeftUp:
                    MouseKeyboard.MouseSimulator.X = mouseEvent.X;
                    MouseKeyboard.MouseSimulator.Y = mouseEvent.Y;
                    MouseKeyboard.MouseSimulator.MouseUp(MouseButtons.Left);
                    break;
                case RemoteDesketOrder.OrderType.MouseRightDown:
                    MouseKeyboard.MouseSimulator.X = mouseEvent.X;
                    MouseKeyboard.MouseSimulator.Y = mouseEvent.Y;
                    MouseKeyboard.MouseSimulator.MouseDown(MouseButtons.Right);
                    break;
                case RemoteDesketOrder.OrderType.MouseRightUp:
                    MouseKeyboard.MouseSimulator.X = mouseEvent.X;
                    MouseKeyboard.MouseSimulator.Y = mouseEvent.Y;
                    MouseKeyboard.MouseSimulator.MouseUp(MouseButtons.Right);
                    break;
                case RemoteDesketOrder.OrderType.MouseWheel:
                    MouseKeyboard.MouseSimulator.X = mouseEvent.X;
                    MouseKeyboard.MouseSimulator.Y = mouseEvent.Y;
                    MouseKeyboard.MouseSimulator.MouseWheel(0);
                    break;
            }
        }
        #endregion

        #region 收到控制命令，处理命令
        /// <summary>
        /// 收到控制命令，处理命令
        /// </summary>
        /// <param name="DesketOrder"></param>
        private void ExcuteOrder(RemoteDesketOrder DesketOrder)
        {
            if (DesketOrder.orderType == RemoteDesketOrder.OrderType.KeyDown)
            {
                MouseKeyboard.KeyboardSimulator.KeyDown((Keys)DesketOrder.KeyCode);
            }
            else if (DesketOrder.orderType ==RemoteDesketOrder.OrderType.KeyUp)
            {
                MouseKeyboard.KeyboardSimulator.KeyUp((Keys)DesketOrder.KeyCode);
            }
            else
            {
                MouseWork(DesketOrder);
            }
        }
        #endregion

        #region 发送远程控制命令
        /// <summary>
        /// 发送远程控制命令
        /// </summary>
        /// <param name="DesketOrder"></param>
        private void sendControlPacket(RemoteDesketOrder DesketOrder)
        {
            ImagePacket  pak = new  ImagePacket();
            pak.CommandType = (byte)TransmitType.RemoteControl;
            pak.Payload = DesketOrder.ToArray();
            SendConnectData(pak.ToArray());
        }
        #endregion

        #region 公共方法

        #region UDP服务
        /// <summary>
        /// 开始
        /// </summary>
        public void StartConnect()
        {
            if (IsIni) return;
            IsIni = true;


            #region 收到数据事件
            RecivedData += (s, e) =>
            {
                ReceiveData(e.Data);//执行收到数据事件
            };
            #endregion

            #region 网络成功联接事件
            Connected += (s, e) =>
            {
                if (IsControler)
                    RequestSendDesktop();
            };
            #endregion 

            this.Start();
        } 
        #endregion 

        #region 设置远程主机信息和远程屏幕尺寸
        /// <summary>
        /// 设置远程主机信息和远程屏幕尺寸
        /// </summary>
        /// <param name="hostID">P2P主机信息</param>
        /// <param name="remoteDesktopWidth"></param>
        /// <param name="remoteDesktopHeight"></param>
        public void setRemoteHostIDandScreen(long hostID, int remoteDesktopWidth = 0, int remoteDesktopHeight = 0)
        {
            if (IsControler)//如果是控制者
            {
                RemoteDesktopWidth = remoteDesktopWidth;
                RemoteDesktopHeight = remoteDesktopHeight;
                float screenPix = (float)RemoteDesktopWidth / (float)RemoteDesktopHeight;
                if (screenPix >= 1.6)
                {
                    uX = (float)RemoteDesktopWidth / 1280;//设置放大系数
                    uY = (float)RemoteDesktopHeight / 720;//设置放大系数
                }
                else
                {
                    uX = (float)RemoteDesktopWidth / 1024;//设置放大系数
                    uY = (float)RemoteDesktopHeight / 768;//设置放大系数
                }
            }

            //if (!IsControler)//如果是被控者
            //    StartConnect();

            RequestNATIntroduction(hostID);
        }
        #endregion

        #region 发送键盘键值
        /// <summary>
        /// 发送键盘键值
        /// </summary>
        /// <param name="key"></param>
        public void sendKeyUp(Keys key)
        {
            RemoteDesketOrder DesketOrder = new RemoteDesketOrder();
            DesketOrder.orderType = RemoteDesketOrder.OrderType.KeyUp;
            DesketOrder.KeyCode = (byte)key;
            sendControlPacket(DesketOrder);//发送命令
        }
        /// <summary>
        /// 发送键盘键值
        /// </summary>
        /// <param name="key"></param>
        public void sendKeyDown(Keys key)
        {
            RemoteDesketOrder DesketOrder = new RemoteDesketOrder();
            DesketOrder.orderType = RemoteDesketOrder.OrderType.KeyDown;
            DesketOrder.KeyCode = (byte)key;
            sendControlPacket(DesketOrder);//发送命令
        }
        #endregion

        #region 取消传输
        /// <summary>
        /// 取消传输
        /// </summary>
        public void CancelTransmit()
        {
            IsCancelTransmit = true;//标识文件发送结束  
            Stop();
        }
        #endregion

        #endregion 
    } 
}
